package Storage;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import org.json.JSONObject;
import Object.DSException;

//@author A0103535R
/************************************ Abstract Class DSSettingFileHandler ***************************/
public class DSSettingFileHandler {
	private static final String SETTING_BACKGROUND = "background";
	private static final String SETTING_ANIMATION = "isAnimated";
	private static final String SETTING_FILE_PATH = "setting.txt";
	private static final int SETTING_BACKGROUND_DEFAULT = 1;
	private static final boolean SETTING_ANIMATION_DEFAULT = true;

	private static DSSettingFileHandler _settingFileHandler = null;
	private File _settingFile = null;
	private BufferedWriter _settingBufferedWriter = null;
	private BufferedReader _settingBufferedReader = null;
	private JSONObject _jsonObject = new JSONObject();

	/**
	 * This is the constructor of DSSettingFileHandler.
	 * 
	 * @throws DSException
	 */
	private DSSettingFileHandler() throws DSException {
		if (_settingFile == null) {
			initSettingFile();
		}
		if (_settingBufferedReader == null) {
			initSettingBufferedReader();
		}
		assert _settingFile != null;
		assert _settingBufferedReader != null;
	}

	/**
	 * This function is used to get the instance singleton of
	 * DSSettingFileHandler
	 * 
	 * @return the singleton instance of DSSettingFileHandler
	 * 
	 * @throws DSException
	 */
	public static DSSettingFileHandler getInstance() throws DSException {
		if (_settingFileHandler == null) {
			_settingFileHandler = new DSSettingFileHandler();
		}
		assert _settingFileHandler != null;
		return _settingFileHandler;
	}

	/**
	 * This function is used to initialize setting file or create a new one if
	 * it not exists.
	 * 
	 * @throws DSException
	 */
	private void initSettingFile() throws DSException {
		try {
			_settingFile = new File(SETTING_FILE_PATH);
			if (!_settingFile.exists()) {
				if (!_settingFile.createNewFile()) {
					throw new IOException();
				}
			}
			assert _settingFile != null;
		} catch (IOException e) {
			throw new DSException(
					DSException.EXCEPTION_MESSAGE_DSSETTINGFILEHANDLER_INIT_SETTING_FILE);
		}
	}

	/**
	 * This function is used to initialize the buffered reader.
	 * 
	 * @throws DSException
	 */
	private void initSettingBufferedReader() throws DSException {
		try {
			_settingBufferedReader = new BufferedReader(new InputStreamReader(
					new FileInputStream(_settingFile)));
			assert _settingBufferedReader != null;
		} catch (FileNotFoundException e) {
			throw new DSException(
					DSException.EXCEPTION_MESSAGE_DSSETTINGFILEHANDLER_INIT_SETTING_BUFFERED_READER);
		}
	}

	/**
	 * This function is used to initialize the buffered writer.
	 * 
	 * @throws DSException
	 */
	private void initSettingBufferedWriter() throws DSException {
		try {
			_settingBufferedWriter = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(_settingFile, false)));
			assert _settingBufferedWriter != null;
		} catch (FileNotFoundException e) {
			throw new DSException(
					DSException.EXCEPTION_MESSAGE_DSSETTINGFILEHANDLER_INIT_SETTING_BUFFERED_WRITER);
		}
	}

	/**
	 * This function is used to re-initialize the buffered writer.
	 * 
	 * @throws DSException
	 */
	private void recreateSettingBufferedWriter() throws DSException {
		initSettingBufferedWriter();
	}

	/**
	 * This function is used to load settings from local file.
	 * 
	 * @throws DSException
	 */
	private void loadFromSettingFile() throws DSException {
		try {
			String lineString = _settingBufferedReader.readLine();
			if(lineString != null){
				_jsonObject = new JSONObject(lineString);
			}
		} catch (IOException e) {
			throw new DSException(
					DSException.EXCEPTION_MESSAGE_DSSETTINGFILEHANDLER_LOAD_FROM_SETTING_FILE);
		}
	}

	/**
	 * This function is used to load the background setting from local file.
	 * 
	 * @return the setting of background stored.
	 * 
	 * @throws DSException
	 */
	public int loadBackgroundNumFromSettingFile() throws DSException {
		loadFromSettingFile();
		if(_jsonObject.has(SETTING_BACKGROUND)){
			return (int) _jsonObject.get(SETTING_BACKGROUND);
		}
		return SETTING_BACKGROUND_DEFAULT;
	}

	/**
	 * This function is used to load the animation setting from local file.
	 * 
	 * @return the setting of animation stored.
	 * 
	 * @throws DSException
	 */
	public boolean loadIsAnimatedFromSettingFile() throws DSException {
		loadFromSettingFile();
		if(_jsonObject.has(SETTING_ANIMATION)){
			return (boolean) _jsonObject.get(SETTING_ANIMATION);
		}
		return SETTING_ANIMATION_DEFAULT;
	}

	/**
	 * This function is used to save settings to local file.
	 * 
	 * @param backgroundNum
	 *            the no of background set.
	 * @param isAnimated
	 *            is the animated turned on.
	 *            
	 * @throws DSException
	 */
	public void saveToSettingFile(int backgroundNum, boolean isAnimated)
			throws DSException {
		writeToSettingFile(backgroundNum, isAnimated);
	}

	/**
	 * This function is used to write setting data to local file
	 * 
	 * @param backgroundNum
	 *            the setting of background.
	 * @param isAnimated
	 *            the setting of animation.
	 *            
	 * @throws DSException
	 */
	private void writeToSettingFile(int backgroundNum, boolean isAnimated)
			throws DSException {
		JSONObject jsonObject = convertToJSONObject(backgroundNum, isAnimated);
		recreateSettingBufferedWriter();
		writeJSONObjectToSettingFile(jsonObject);
	}

	/**
	 * This function is used to save the jsonObject including setting data to
	 * local file.
	 * 
	 * @param jsonObject
	 *            the jsonObject to saved in local file.
	 *            
	 * @throws DSException
	 */
	private void writeJSONObjectToSettingFile(JSONObject jsonObject)
			throws DSException {
		try {
			_settingBufferedWriter.write(jsonObject.toString() + "\n");
			_settingBufferedWriter.flush();
		} catch (IOException e) {
			throw new DSException(
					DSException.EXCEPTION_MESSAGE_DSSETTINGFILEHANDLER_WRITE_JSONOBJECT_TO_SETTING_FILE);
		}
	}

	/**
	 * This function is used to convert setting data to JsonObject
	 * 
	 * @param backgroudNum
	 *            the setting of background
	 * @param isAnimated
	 *            the setting of animation
	 * @return the jsonObject including setting data.
	 */
	private JSONObject convertToJSONObject(int backgroundNum, boolean isAnimated) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put(SETTING_BACKGROUND, backgroundNum);
		jsonObject.put(SETTING_ANIMATION, isAnimated);
		return jsonObject;
	}
}
